/*
 * jxDao Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxDao.dao.db;

import java.io.IOException;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.jxDao.dao.Conn;
import org.jxUtils.check.ParameterCheck;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxDao
 * @package org.jxDao.dao.db
 * @date 11/04/2014 - 12:45:39
 */
public abstract class DBMS implements Serializable {
	
	private static final long serialVersionUID = 346057540827282506L;
	
	private static final String SELECT_ALL_TABLE_NAMES_DERBY = "SELECT TABLENAME FROM SYS.SYSTABLES WHERE TABLETYPE = 'T'";
	
	/**
	 * Tabelas e views:
	 * SELECT RDB$RELATION_NAME FROM RDB$RELATIONS;
	 * Somente tabelas:
	 * SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
	 * WHERE RDB$VIEW_BLR IS NULL;
	 * Somente views:
	 * SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
	 * WHERE NOT RDB$VIEW_BLR IS NULL;
	 */
	private static final String SELECT_ALL_TABLE_NAMES_FIREBIRD = "SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$VIEW_BLR IS NULL ORDER BY RDB$RELATION_NAME";
	
	private static final String SELECT_ALL_TABLE_NAMES_MYSQL = "SHOW tables";
	
	private static final String SELECT_ALL_TABLE_NAMES_ORACLE = "SELECT table_name FROM all_tables ORDER BY table_name";
	
	private static final String SELECT_ALL_TABLE_NAMES_POSTGRESQL = "SELECT tablename FROM pg_tables ORDER BY tablename";
	
	private static final String SELECT_ALL_TABLE_NAMES_SQLITE = "SELECT name FROM sqlite_master WHERE type = 'table'";
	
	/**
	 * SELECT TABLE_NAME
	 * FROM information_schema.tables
	 * WHERE TABLE_SCHEMA = 'dbo' AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME
	 */
	private static final String SELECT_ALL_TABLE_NAMES_SQLSERVER = "SELECT TABLE_NAME FROM information_schema.tables ORDER BY TABLE_NAME";
	
	private final Driver driver;
	
	private DBInfo dbInfo;
	
	/**
	 * @param driver
	 * @param dbInfo
	 */
	public DBMS(final Driver driver, final DBInfo dbInfo) {
	
		ParameterCheck.isNull(driver, "driver");
		ParameterCheck.isNull(dbInfo, "dbInfo");
		
		this.driver = driver;
		this.dbInfo = dbInfo;
		this.dbInfo.setDBMS(this);
	}
	
	/**
	 * @return All Table Names List
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	public final List<String> getAllTableName() throws ClassNotFoundException, SQLException, IOException {
	
		final List<String> tables = new ArrayList<>();
		
		//
		try (Conn conn = new Conn(dbInfo)) {
			
			ResultSet resultSet = null;
			
			//
			if(isDerby()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_DERBY);
				
			}else if(isFirebird()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_FIREBIRD);
				
			}else if(isMySQL()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_MYSQL);
				
			}else if(isOracle()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_ORACLE);
				
			}else if(isPostgreSQL()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_POSTGRESQL);
				
			}else if(isSQLite()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_SQLITE);
				
			}else if(isSQLServer()) {
				
				resultSet = conn.executeQuery(SELECT_ALL_TABLE_NAMES_SQLSERVER);
				
			}else {
				
				throw new UnsupportedOperationException();
			}
			
			//
			if(resultSet != null) {
				
				//
				while(resultSet.next()) {
					
					tables.add(resultSet.getString(1));
				}
				
				resultSet.close();
			}
		}
		
		return tables;
	}
	
	/**
	 * @return the dbInfo
	 */
	public final DBInfo getDBInfo() {
	
		return dbInfo;
	}
	
	/**
	 * @return the driver
	 */
	public final Driver getDriver() {
	
		return driver;
	}
	
	/**
	 * @return the url
	 */
	public final String getUrl() {
	
		return URL.getUrl(getDBInfo());
	}
	
	/**
	 * @return true if this DBMS is Derby
	 */
	public boolean isDerby() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is Firebird
	 */
	public boolean isFirebird() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is MySQL
	 */
	public boolean isMySQL() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is Oracle
	 */
	public boolean isOracle() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is PostgreSQL
	 */
	public boolean isPostgreSQL() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is SQLite
	 */
	public boolean isSQLite() {
	
		return false;
	}
	
	/**
	 * @return true if this DBMS is SQLServer
	 */
	public boolean isSQLServer() {
	
		return false;
	}
	
	/**
	 * @param dbInfo
	 *        the dbInfo to set
	 */
	public final void setDBInfo(final DBInfo dbInfo) {
	
		this.dbInfo = dbInfo;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
	
		return getDriver().toString();
	}
	
	/**
	 * @param driver
	 * @param dbInfo
	 * @return DBMS
	 */
	public static DBMS getDBMS(final Driver driver, final DBInfo dbInfo) {
	
		ParameterCheck.isNull(driver, "driver");
		ParameterCheck.isNull(dbInfo, "dbInfo");
		
		//
		if(driver.equals(Driver.DERBY)) {
			
			return new Derby(dbInfo);
			
		}else if(driver.equals(Driver.FIREBIRD)) {
			
			return new Firebird(dbInfo);
			
		}else if(driver.equals(Driver.MYSQL)) {
			
			return new MySQL(dbInfo);
			
		}else if(driver.equals(Driver.ORACLE)) {
			
			return new Oracle(dbInfo);
			
		}else if(driver.equals(Driver.POSTGRESQL)) {
			
			return new PostgreSQL(dbInfo);
			
		}else if(driver.equals(Driver.SQLITE)) {
			
			return new SQLite(dbInfo);
			
		}else {
			
			return new SQLServer(dbInfo);
		}
	}
}
